import pygame, random, sys, math
from pygame.locals import  *

gameStates = {  0 : "MAINMENU" , 1 : "GAME" , 2 : "EDITOR" }
STATE_MENU, STATE_GAME, STATE_EDITOR = 0 , 1, 2 
GAME_STATE = STATE_EDITOR
debug = True
pygame.init()

if (debug): print("DEBUG MODE IS ON")
if (debug): print("GAME STATE: ", gameStates[GAME_STATE])

if (GAME_STATE): screen_size = (32*20+320,32*20)
else :screen_size = (32*20,32*20)
screen = pygame.display.set_mode(screen_size)

pygame.display.set_caption("My First Game With Oscar")

background = pygame.Surface(screen_size)

background.fill((125,125,125))

pygame.mouse.set_visible(True)

random.seed()

clock = pygame.time.Clock()




DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT = 3,0,1,2
directionMap = { DIR_UP : (0,-1) , DIR_DOWN : (0,1), DIR_LEFT : (-1,0), DIR_RIGHT : (1,0) }
entIds = 0
class Wall():
    def __init__(self,rect):
        self.rect = rect
        self.id = 0
    def draw(self):
        pygame.draw.rect(screen,(255,0,0),self.rect,1)

class WallManager():
    def __init__(self):
        self.objects = list()
        
    def add(self,o):
        global entIds
        o.id = entIds
        entIds+=1
        self.objects.append(o)
    def draw(self):
        for o in self.objects:
            o.draw()
    def checkCollision(self,ob):
        r = ob.tempRect
        if (r is None): r = ob.rect
        for o in self.objects:
            if (o.rect.colliderect(r)):
                if (o.id != ob.id):
                    return True
        return False
  
class Player():
    def __init__(self,x,y,image):
        self.rect = Rect(x,y,32,32)
        self.image = image
        self.direction = DIR_DOWN
        self.frame = [1,self.direction*32,32,32]
        self.cFrame = 1
        self.speed = 2
        self.id = -1
        self.maxHp = 100
        self.hp = 100
        self.maxMana = 50.0
        self.mana = 50.0
        self.tempRect = None
        self.lastCollision = None

        self.damage = 1

        self.manaRegen = 0.1
        self.level = 1
        self.exp = 0
        self.lexp = 20
        self.tnl = self.lexp - self.exp
        self.texp = self.exp

    def addExp(self,exp):
        self.exp+=exp
        self.texp+=exp
        if (self.exp >= self.lexp):
            t = self.exp-self.lexp
            self.level+=1
            self.exp = 0 + t
            self.lexp*=2
        tnl = self.lexp - self.exp
            
    def addHealth(self,hp):
        self.hp += hp
        if (self.hp > self.maxHp):
            self.hp = self.maxHp
        elif (self.hp < 1):
            self.hp = 0
    def setHealth(self,hp):
        self.hp = hp
    def addMana(self,mana):
        self.mana += mana
        if (self.mana > self.maxMana):
            self.mana = self.maxMana
        elif (self.mana < 0.1):
            self.mana = 0
    def setMana(self,mana):
        self.mana = mana
    def drawHealthBar(self):
        h = Rect(self.rect.x-5,self.rect.y-5,50*(self.hp/self.maxHp),5)
        b = Rect(self.rect.x-5,self.rect.y-5,50,5)

        pygame.draw.rect(screen,(220,0,0),h)
        pygame.draw.rect(screen,(0,0,0),b,1) 
    def draw(self):
        #pygame.draw.rect(screen, (0,0,255), self.rect)
        screen.blit(self.image,self.rect,self.frame)
        self.drawHealthBar()
    
        
    def setDirection(self,direction):
        self.direction = direction
        self.frame = [self.cFrame,self.direction*32,32,32]
        
    def nextFrame(self):
        self.cFrame+=32
        if (self.cFrame > 2*32): self.cFrame = 0
        self.frame[0] = self.cFrame
    def checkMove(self,direction,wman):
        self.tempRect = Rect(self.rect.x+(directionMap[direction][0]*self.speed),self.rect.y +directionMap[direction][1]*self.speed,32,32)
        return not(wman.checkCollision(self))
    def update(self):
        self.addMana(self.manaRegen) 
    def move(self,direction,wman):
        self.setDirection(direction)
        self.nextFrame()

        if (self.checkMove(direction,wman)):
            self.rect.x += directionMap[direction][0]*self.speed
            self.rect.y +=directionMap[direction][1]*self.speed
            


playerImage = pygame.image.load("gfx/character.png")
tileImage = pygame.image.load("gfx/tileSet2.png")


if (debug): print ("Loading image files...")

smallFont = pygame.font.SysFont("monospace", 12)
medFont = pygame.font.SysFont("monospace", 15)
largeFont = pygame.font.SysFont("monospace", 20)
if (debug): print ("Loaded fonts...")


tileIndex = list()
wallManager = WallManager()





track = 0

tileMap = [
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,15,15,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,16,16,0,0,0,0,0,0,0,0,0,0]]

collisionMap = [
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0]]


if (debug): print(len(tileMap[0]),"x",len(tileMap)," map loaded.")
def loadMapIndexes(timage):
    tmap = list()
    for  i in range(0,timage.get_height()//32):
        for j in range(0,timage.get_width()//32):
            tmap.append((j*32,i*32,32,32))
    return tmap
            
tileIndex = loadMapIndexes(tileImage)
if (debug): print(len(tileIndex), " tiles indexed.")
def drawMap(tmap,timage):
    for idx, i in enumerate(tmap):
        for idx2,j in enumerate(i):
            screen.blit(timage,(idx2*32,idx*32),tileIndex[j])
            if (GAME_STATE == STATE_EDITOR): pygame.draw.rect(screen,(0,0,0),(idx2*32,idx*32,32,32),1)

def loadObjects(omap):
    wallManager.objects = list()
    count = 0
    for idx, o in enumerate(omap):
        for idx2, d in enumerate(o):
            if (d == 1):
                wallManager.add(Wall(Rect(idx2*32,idx*32,32,32)))
                count+=1
    print(count,"objects loaded...")

loadObjects(collisionMap)
            

def indexEditor(tileIndex):
    x = 0
    y = 0
    count = 0
    editorIndex = list()
    editorIndex.append(list())
    
    for idx,t in enumerate(tileIndex):
        #screen.blit(timage,(x*32+(32*20),y*32),t)
        editorIndex[count].append([((x*32+(32*20),y*32)),t,idx])
        x+=1
        
        if (x > 10):
            x =0
            y+=1
            editorIndex.append(list())
            count+=1
    return editorIndex

editorIndex = indexEditor(tileIndex)
print("Editor tiles indexed.")
def drawEditor(editorIndex,timage):
    for i, t in enumerate(editorIndex):
        for i2, t2 in enumerate(t):
            screen.blit(timage,t2[0],t2[1])
            
        
def saveMap():
    file = open('map.txt','w')
    for l in tileMap:
        for i in l:
            file.write(str(i)+",")
        file.write("\n")
    file.close()
    print("saved to map.txt.")

def readMap():
    file = open('map.txt','r')
    tMap = list()
    tMap.append(list())
    count = 0
    for line in file:
        data = line.split(",")
        for i in range(0,20):
            #print(data[i])
            tMap[count].append(int(data[i]))
        count+=1
        tMap.append(list())
    file.close()
    print("read from map.txt")
    return tMap

def saveObjectMap():
    file = open('objectmap.txt','w')
    for l in collisionMap:
        for i in l:
            file.write(str(i)+",")
        file.write("\n")
    file.close()
    print("saved to objectmap.txt.")

def readObjectMap():
    file = open('objectmap.txt','r')
    tMap = list()
    tMap.append(list())
    count = 0
    for line in file:
        data = line.split(",")
        for i in range(0,20):
            #print(data[i])
            tMap[count].append(int(data[i]))
        count+=1
        tMap.append(list())
    file.close()
    print("read from objectmap.txt")
    return tMap
        

    
    
def grabEditorTile(mouse_x,mouse_y):
    x = ((mouse_x-(20*32))//32)
    y = ((mouse_y)//32)

    
    if (x >=0 and y >= 0): return editorIndex[y][x];
    return None
def inMap(x,y):
    if (x >= 0 and x <= 20*32) and (y >=0 and y <= 20*32):
        return True
    return False
def inEditor(x,y):
    if (x >= 20*32 and x <= 20*32+320) and (y >= 0 and y <=20*32):
        return True
    return False
def placeTile(x,y,td):
    tx = x //32
    ty = y //32

    #print(tx,ty,td[2])
    tileMap[ty][tx] = td[2]
    
def placeWall(x,y):
    tx = x //32
    ty = y //32

    #print(tx,ty,td[2])
    if (collisionMap[ty][tx] == 1): collisionMap[ty][tx] = 0
    else :collisionMap[ty][tx] = 1
    loadObjects(collisionMap)
def drawGUI():
    def drawNamePlate():
        backplate = Rect(32*20,0,320,150)
        pygame.draw.rect(screen,(102,51,0),backplate)
        text =  medFont.render("Player", 1, (0,250,0))
        ltext = medFont.render("Level:" + str(player.level), 1, (255,255,0))
        screen.blit(text,(32*20+5,45))
        screen.blit(ltext,(32*20+5,65))
    def drawPlayerAvatar():
        p = Rect(32*20+25,5,32,32)
        screen.blit(player.image,p,player.frame)
    def drawHealthBar():
        h = Rect(32*20+75,5,200*(player.hp/player.maxHp),25)
        b = Rect(32*20+75,5,200,25)
        color = (0,220,0)
        if (player.maxHp*0.6 >= player.hp): color = (250,100,0)
        if (player.maxHp*0.3 >= player.hp): color = (250,0,0)
        pygame.draw.rect(screen,color,h)
        pygame.draw.rect(screen,(0,0,0),b,2)
    def drawManaBar():
        h = Rect(32*20+75,35,200*(player.mana/player.maxMana),25)
        b = Rect(32*20+75,35,200,25)

        pygame.draw.rect(screen,(0,0,200),h)
        pygame.draw.rect(screen,(0,0,0),b,2)
    def drawExpBar():
        h = Rect(32*20+75,65,200*(player.exp/player.lexp),25)
        b = Rect(32*20+75,65,200,25)

        pygame.draw.rect(screen,(200,200,0),h)
        pygame.draw.rect(screen,(0,0,0),b,2)
    
    drawNamePlate()
    drawPlayerAvatar()
    drawHealthBar()
    drawManaBar()
    drawExpBar()
    
    
#tileMap = readMap()
#collisionMap = readObjectMap()
loadObjects(collisionMap)
player = Player(0,0,playerImage)

currentTile = None

while True:
    mouse_x,mouse_y = pygame.mouse.get_pos()[0],pygame.mouse.get_pos()[1]
    
    for event in pygame.event.get():
        if (event.type == QUIT):
            pygame.quit()
            sys.exit()
        elif (event.type == MOUSEBUTTONDOWN):
            if (event.button == 1):
                if (inMap(mouse_x,mouse_y)and GAME_STATE == STATE_EDITOR):
                    if (currentTile is not None): placeTile(mouse_x,mouse_y,currentTile)
                elif(inEditor(mouse_x,mouse_y)):
                    currentTile = grabEditorTile(mouse_x,mouse_y)
            else  :
                if (inMap(mouse_x,mouse_y) and GAME_STATE == STATE_EDITOR):
                    placeWall(mouse_x,mouse_y)
            

    keyState = pygame.key.get_pressed()
    if (keyState[pygame.K_UP]):
        player.move(DIR_UP,wallManager)
    elif (keyState[pygame.K_DOWN]):
        player.move(DIR_DOWN,wallManager)
    if (keyState[pygame.K_LEFT]):
        player.move(DIR_LEFT,wallManager)
    elif (keyState[pygame.K_RIGHT]):
        player.move(DIR_RIGHT,wallManager)
    if (keyState[pygame.K_w]):
        print(tileMap)
    if (keyState[pygame.K_f]):
        print('You pressed the fuck key')
        #blastManager.add(Blast(player.rect.x,player.rect.y,blastImageD,DIR_DOWN))
    if (keyState[pygame.K_s]):
        if (GAME_STATE == STATE_EDITOR or debug):
            saveObjectMap()
            saveMap()
    if (keyState[pygame.K_r]):
        if (GAME_STATE == STATE_EDITOR or debug):
            tileMap = readMap()
            collisionMap = readObjectMap()
            loadObjects(collisionMap)
    if (keyState[pygame.K_m]):
        if (debug):
            player.addHealth(-1)
            player.addMana(-1)
    if (keyState[pygame.K_n]):
        if (debug):
            player.addHealth(1)
            player.addMana(1)
            
  
    player.update()
    time_passed = clock.tick(60)/1000 #FPS

    screen.blit(background, (0,0))
    drawMap(tileMap,tileImage)
    #screen.blit(tileImage,(0,0,32,32),tileIndex[track])
    if (GAME_STATE == STATE_EDITOR):
        drawEditor(editorIndex,tileImage)
        if (currentTile is not None):
            screen.blit(tileImage,(mouse_x,mouse_y),currentTile[1])
        pygame.draw.rect(screen, (0,0,0), (mouse_x,mouse_y,32,32),1)
    wallManager.draw()
    player.draw()
    if (GAME_STATE == STATE_GAME):
        
        drawGUI()
    pygame.display.update()
